<?php

namespace App\Http\Controllers\Api\Miniprogram;

use App\Http\Controllers\Controller;
use App\Models\Marketing\Coupon;
use App\Models\Order\GoodsOrder;
use App\Models\System\CheckItem;
use App\Models\Wechat\WechatMiniUser;
use App\Service\MiniprogramService;
use App\Service\OrderService;
use App\Util\Help;
use EasyWeChat\Pay\Application;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use WeChatPay\Builder;
use WeChatPay\Crypto\Rsa;
use WeChatPay\Formatter;
use WeChatPay\Util\PemUtil;

class OrderController extends Controller
{
    private function generateOrderNo(): string
    {
        return date('YmdHis') . random_int(100000, 999999);
    }

    public function generateOrder()
    {
        $userId = Auth::id();
        $orderNo = $this->generateOrderNo();
        $checkItemId = request('check_item_id');
        $locationType = request('location_type', 0);
        $checkItem = CheckItem::where('status', 1)->where('id', $checkItemId)->first();
        if (!$checkItem) {
            return $this->failJson('check_item.check_item_not_enable');
        }
        $price = round($checkItem->price + ($locationType ? env('DOOR_TO_DOOR_SERVICE', 200) : 0), 2);
        $goodsOrder = GoodsOrder::create([
            'order_no' => $orderNo,
            'user_id' => $userId,
            'sum_price' => $price,
            'discount' => 0,
            'location_type' => $locationType,
            'final_price' => $price,
            'payment_status' => 0,
            'payment_result' => '',
        ]);
        $goodsOrder->checkItems()->create([
            'check_item_id' => $checkItemId,
            'thumb_id' => $checkItem->thumb_id,
            'name' => $checkItem->name,
            'price' => $price,
            'amount' => 1,
            'sum_price' => $price,
        ]);
        return $this->successJson($goodsOrder);
    }

    public function getOrder()
    {
        $userId = Auth::id();
        $id = request('id');
        $goodsOrder = GoodsOrder::with(['checkItems.checkItem', 'checkItems.thumb', 'samples.checkItem', 'samples.qrcode', 'samples.binder'])
            ->where('user_id', $userId)->where('id', $id)->first();
        if (!$goodsOrder) {
            return $this->failJson('order.order_not_exist');
        }
        $goodsOrder->status_options = trans('order-sample.options.status');
        return $this->successJson($goodsOrder);
    }

    public function modifyOrder()
    {
        $userId = Auth::id();
        $orderNo = request('order_no');
        $amount = request('amount');
        $couponCode = request('coupon_code');
        $locationType = request('location_type', 0);
        $goodsOrder = GoodsOrder::with(['checkItems.checkItem', 'checkItems.thumb'])->where('user_id', $userId)->where('order_no', $orderNo)->first();
        if (!$goodsOrder) {
            return $this->failJson('order.order_not_exist');
        }
        if ($goodsOrder->payment_status >= 2) {
            return $this->failJson('order.order_not_modify');
        }
        $checkItemId = request('check_item_id');
        $checkItem = CheckItem::where('status', 1)->where('id', $checkItemId)->first();
        if (!$checkItem) {
            return $this->failJson('check_item.check_item_not_enable');
        }
        $price = round($checkItem->price + ($locationType ? env('DOOR_TO_DOOR_SERVICE', 200) : 0), 2);
        $sumPrice = round($price * $amount, 2);
        $goodsOrder->checkItems->each->update([
            'check_item_id' => $checkItemId,
            'thumb_id' => $checkItem->thumb_id,
            'name' => $checkItem->name,
            'price' => $price,
            'amount' => $amount,
            'sum_price' => $sumPrice,
        ]);
        $goodsOrder->update([
            'location_type' => $locationType,
            'sum_price' => $sumPrice,
            'remark' => request('remark', ''),
        ]);
        if ($couponCode) {
            $coupon = Coupon::where('code', $couponCode)->where('status', 1)->first();
            if (!$coupon) {
                return $this->failJson('coupon.coupon_not_enable');
            }
            if ($coupon->reached_amount > $goodsOrder->sum_price) {
                return $this->failJson('coupon.coupon_not_reached');
            }
            $goodsOrder->update([
                'coupon_code' => $couponCode,
                'discount' => $coupon->discount,
                'final_price' => round($sumPrice - $coupon->discount, 2),
            ]);
        } else {
            $goodsOrder->update([
                'coupon_code' => '',
                'discount' => 0,
                'final_price' => $sumPrice,
            ]);
        }
        return $this->successJson($goodsOrder);
    }

    public function getList()
    {
        $userId = Auth::id();
        $paymentStatus = request('type', 0);
        GoodsOrder::where('user_id', $userId)->whereIn('payment_status', [0, 2, 6])->get()->each(function ($goodsOrder) {
            if ($goodsOrder->payment_status == 0) {
                if ($goodsOrder->created_at->addMinutes(60 * 24) < now()) {
                    $goodsOrder->delete();
                }
            } else {
                $samples = $goodsOrder->samples;
                $flag = true;
                $samples->each(function ($sample) use (&$flag) {
                    if ($sample->status != 4) {
                        $flag = false;
                    }
                });
                if ($flag) {
                    $goodsOrder->payment_status = 6;
                    $goodsOrder->save();
                } else {
                    if ($goodsOrder->payment_status == 6) {
                        $goodsOrder->payment_status = 2;
                        $goodsOrder->save();
                    }
                }
            }
        });
        $model = GoodsOrder::with(['user.wechatMiniUser', 'checkItems.thumb'])->where('user_id', $userId)->orderByDesc('id');
        if ($paymentStatus == 1) {
            $model = $model->whereIn('payment_status', [0, 1]);
        } else if ($paymentStatus == 2) {
            $model = $model->where('payment_status', 2);
        } else if ($paymentStatus == 3) {
            $model = $model->where('payment_status', 6);
        }
        $goodsOrders = $model->paginate(20);
        return $this->successJson([
            'list' => $goodsOrders,
            'payment_status' => trans('goods-order.options.payment_status'),
        ]);
    }

    public function deleteOrder()
    {
        $userId = Auth::id();
        $id = request('id');
        $order = GoodsOrder::where('user_id', $userId)->where('id', $id)->first();
        if (!$order) {
            return $this->failJson('order.order_not_exist');
        }
        if ($order->payment_status != 0 && $order->payment_status != 1) {
            return $this->failJson('order.order_not_delete');
        }
        $order->payment_status = 5;
        $order->save();
        $order->delete();
        return $this->successJson();
    }

    private function payInstance()
    {
        $merchantId = config('wechatpay.merchantId');

        $merchantPrivateKeyFilePath = config('wechatpay.merchantPrivateKeyFilePath');
        $merchantPrivateKeyInstance = Rsa::from($merchantPrivateKeyFilePath, Rsa::KEY_TYPE_PRIVATE);

        $merchantCertificateSerial = config('wechatpay.merchantCertificateSerial');

        $platformCertificateFilePath = config('wechatpay.platformCertificateFilePath');
        $platformPublicKeyInstance = Rsa::from($platformCertificateFilePath, Rsa::KEY_TYPE_PUBLIC);

        $platformCertificateSerial = PemUtil::parseCertificateSerialNo($platformCertificateFilePath);

        return Builder::factory([
            'mchid'      => $merchantId,
            'serial'     => $merchantCertificateSerial,
            'privateKey' => $merchantPrivateKeyInstance,
            'certs'      => [
                $platformCertificateSerial => $platformPublicKeyInstance,
            ],
        ]);
    }

    public function doPay()
    {
        $config = config('wechatpay');
        $userId = Auth::id();
        $orderNo = request('order_no');
        $goodsOrder = GoodsOrder::with(['user.wechatMiniUser'])->where('user_id', $userId)->where('order_no', $orderNo)->first();
        $wechatMiniUser = $goodsOrder->user->wechatMiniUser;
//        Log::debug('goodsOrder', [$goodsOrder]);
        $app = new Application($config);
        $response = $app->getClient()->postJson("v3/pay/transactions/jsapi", [
            "mchid" => $config['mch_id'],
            "out_trade_no" => $goodsOrder->order_no,
            "appid" => $config['appid'],
            "description" => $goodsOrder->checkItems[0]->name,
            "notify_url" => url('api/v1/mini/payment_notify'),
            "amount" => [
                "total" => intval($goodsOrder->final_price * 100),
                "currency" => "CNY"
            ],
            "payer" => [
                "openid" => $wechatMiniUser[0]->openid
            ]
        ]);
        $payResp = $response->toArray(false);
        $appid = $config['appid'];
        $timestamp = (string)Formatter::timestamp();
        $nonceStr = Formatter::nonce();
        $package = 'prepay_id=' . $payResp['prepay_id'];
        $privateKey = file_get_contents($config['private_key']);
//        $dataToSign = "$appid\n$timestamp\n$nonceStr\n$package";
//        openssl_sign($dataToSign, $rawSign, $privateKey, OPENSSL_ALGO_SHA256);
//        $paySign = base64_encode($rawSign);
//        Log::debug('paySign:', [$dataToSign, $rawSign, $paySign]);
        $params = [
            'appId'     => $appid,
            'timeStamp' => $timestamp,
            'nonceStr'  => $nonceStr,
            'package'   => $package,
        ];
        $params += ['paySign' => Rsa::sign(
            Formatter::joinedByLineFeed(...array_values($params)),
            $privateKey
        ), 'signType' => 'RSA'];
        Log::debug('paySign:', $params);
//        $result = [
//            'timeStamp' => $timestamp,
//            'nonceStr' => $nonceStr,
//            'package' => $package,
//            'signType' => 'RSA',
//            'paySign' => $paySign,
//        ];
        return $this->successJson($params);
    }

    public function payFinish()
    {
        $userId = Auth::id();
        $id = request('id');
        $order = GoodsOrder::where('user_id', $userId)->where('id', $id)->first();
        if (!$order) {
            return $this->failJson('order.order_not_exist');
        }
        if ($order->payment_status != 2) {
            return $this->failJson('order.order_not_pay_success');
        }
//        if ($order->payment_status != 0 && $order->payment_status != 1) {
//            return $this->failJson('order.order_not_pay');
//        }
//        $order->payment_status = 2;
//        $order->save();

        $miniprogramService = new MiniprogramService();
        $orderCheckItems = $order->checkItems;
        foreach ($orderCheckItems as $orderCheckItem) {
            $amount = $orderCheckItem->amount;
            for ($i = 0; $i < $amount; $i++) {
                $sampleSn = Help::generateSampleSn();
                $qrcode = $miniprogramService->drawQrcode(url('s/' . $sampleSn));
                $order->samples()->create([
                    'sample_sn' => $sampleSn,
                    'item_id' => $orderCheckItem->checkItem->id,
                    'qrcode_id' => $qrcode->id,
                    'check_type' => $order->location_type ? 2 : 1,
                    'status' => 1,
                ]);
            }
        }
        return $this->successJson();
    }

    public function downloadOrderQrcodeFiles()
    {
        $id = request('id');
        $order = GoodsOrder::where('id', $id)->first();
        if (!$order) {
            return $this->failJson('order.order_not_exist');
        }
        $orderSamples = $order->samples;
        $zip = new \ZipArchive();
        $zipName = $order->order_no . '_qrcode_files.zip';
        $zipPath = storage_path('app/public/' . $zipName);
        $disk = Storage::disk();
        if ($zip->open($zipPath, \ZipArchive::CREATE) === true) {
            foreach ($orderSamples as $orderSample) {
                $qrcode = $orderSample->qrcode;
                $qrcodePath = $disk->path($qrcode->file->url);
                $zip->addFile($qrcodePath, $orderSample->sample_sn . '.png');
            }
            $zip->close();
        }
        return response()->download($zipPath, $zipName);
    }

    public function setStatus()
    {
        $orderNo = request('order_no');

    }

    public function paymentNotify(Request $request)
    {
        $config = config('wechatpay');
        $app = new Application($config);
        // $app 为你实例化的支付对象，此处省略实例化步骤
        $server = $app->getServer();

        // 处理支付结果事件
        $server->handlePaid(function ($message) use ($app) {
            Log::debug('payment_notify', [$message]);
            // $message 为微信推送的通知结果，详看微信官方文档

            // 微信支付订单号 $message['transaction_id']
            // 商户订单号 $message['out_trade_no']
            // 商户号 $message['mchid']
            // 具体看微信官方文档...
            // 进行业务处理，如存数据库等...

            try{
                // 验证通过，业务处理
                $outTradeNo = $message->out_trade_no;
                $openid = $message->payer['openid'];

                Log::debug('valid success', [$outTradeNo, $openid]);

                $wechatMiniUser = WechatMiniUser::with(['user'])->where('openid', $openid)->first();
                if (!$wechatMiniUser) {
                    return;
                }
                $goodsOrder = GoodsOrder::where('order_no', $outTradeNo)->where('user_id', $wechatMiniUser->user->id)->first();
                if (!$goodsOrder) {
                    return;
                }
                $goodsOrder->payment_status = 2;
                $goodsOrder->payment_result = 'success';
                $goodsOrder->save();

                Log::debug('pay save success');
            } catch(Exception $e){
                // 验证失败
            }
        });

        // 处理退款结果事件
        $server->handleRefunded(function ($message) {
            // 同上，$message 详看微信官方文档
            // 进行业务处理，如存数据库等...
        });

        return $server->serve();
    }

    public function doRefund()
    {
        $id = request('id');
        $orderService = new OrderService();
        $error = $orderService->refund($id, Auth::id(), '申请退款');
        if ($error) {
            return $this->failJson($error);
        }
        return $this->successJson();
    }
}
